#! /usr/bin/env python

import sys
from optparse import OptionParser
import random
import math

from common.zerror import zassert
from common.zutils import convert


def mustbepowerof2(bits, size, msg):
    if math.pow(2, bits) != size:
        zassert(False, 'Error in argument: %s' % msg)


def mustbemultipleof(bignum, num, msg):
    if int(float(bignum) / float(num)) != (int(bignum) / int(num)):
        zassert(False, 'Error in argument: %s' % msg)


class LinearTranslateCore:
    def __init__(self, addresses='-1', seed=0, asize='16k', physmem='64k', pagesize='4k',
                 numaddrs=5, used=50, verbose=False, compute=False):
        parser = OptionParser()
        parser.add_option('-A', '--addresses', default=addresses,
                          help='a set of comma-separated pages to access; -1 means randomly generate',
                          action='store', type='string', dest='addresses')
        parser.add_option('-s', '--seed', default=seed, help='the random seed', action='store', type='int', dest='seed')
        parser.add_option('-a', '--asize', default=asize, help='address space size (e.g., 16, 64k, 32m, 1g)', action='store',
                          type='string', dest='asize')
        parser.add_option('-p', '--physmem', default=physmem, help='physical memory size (e.g., 16, 64k, 32m, 1g)',
                          action='store', type='string', dest='psize')
        parser.add_option('-P', '--pagesize', default=pagesize, help='page size (e.g., 4k, 8k, whatever)', action='store',
                          type='string', dest='pagesize')
        parser.add_option('-n', '--numaddrs', default=numaddrs, help='number of virtual addresses to generate', action='store',
                          type='int', dest='num')
        parser.add_option('-u', '--used', default=used, help='percent of virtual address space that is used', action='store',
                          type='int', dest='used')
        parser.add_option('-v', help='verbose mode', action='store_true', default=verbose, dest='verbose')
        parser.add_option('-c', help='compute answers for me', action='store_true', default=compute, dest='solve')

        (self.options, self.args,) = parser.parse_args()
        self.deal_args()

    def deal_args(self):
        options = self.options
        random.seed(options.seed)
        asize = convert(options.asize)
        psize = convert(options.psize)
        pagesize = convert(options.pagesize)
        addresses = str(options.addresses)

        if psize <= 1:
            zassert(False, 'Error: must specify a non-zero physical memory size.')

        if asize < 1:
            zassert(False, 'Error: must specify a non-zero address-space size.')

        if psize <= asize:
            zassert(False, 'Error: physical memory size must be GREATER than address space size (for this simulation)')

        if psize >= convert('1g') or asize >= convert('1g'):
            zassert(False, 'Error: must use smaller sizes (less than 1 GB) for this simulation.')

        mustbemultipleof(asize, pagesize, 'address space must be a multiple of the pagesize')
        mustbemultipleof(psize, pagesize, 'physical memory must be a multiple of the pagesize')

        # print some useful info, like the darn page table
        pages = psize // pagesize
        import array

        used = array.array('i')
        pt = array.array('i')
        for i in range(0, pages):
            used.insert(i, 0)
        vpages = asize // pagesize

        # now, assign some pages of the VA
        vabits = int(math.log(float(asize)) / math.log(2.0))
        mustbepowerof2(vabits, asize, 'address space must be a power of 2')
        pagebits = int(math.log(float(pagesize)) / math.log(2.0))
        mustbepowerof2(pagebits, pagesize, 'page size must be a power of 2')
        vpnbits = vabits - pagebits
        pagemask = (1 << pagebits) - 1

        # import ctypes
        # vpnmask  = ctypes.c_uint32(~pagemask).value
        vpnmask = 0xFFFFFFFF & ~pagemask
        # if vpnmask2 != vpnmask:
        #    print 'ERROR'
        #    exit(1)
        # print 'va:%d page:%d vpn:%d -- %08x %08x' % (vabits, pagebits, vpnbits, vpnmask, pagemask)

        print('页表的格式如下：')
        print('最高位bit(最左bit)是 VALID\n'
              '    若该bit为1, 剩下的bit代表PFN\n'
              '    若该bit为0, 该页面不合法\n'
              '打开额外信息可以看到虚拟页表信息')
        print('页表 (从入口地址 0 到最大地址)')
        for v in range(0, vpages):
            done = 0
            while done == 0:
                if (random.random() * 100.0) > (100.0 - float(options.used)):
                    u = int(pages * random.random())
                    if used[u] == 0:
                        used[u] = 1
                        done = 1
                        # print '%8d - %d' % (v, u)
                        if options.verbose:
                            print('  [%8d]  ' % v, end=' ')
                        else:
                            print('  ', end=' ')
                        print('0x%08x' % (0x80000000 | u))
                        pt.insert(v, u)
                else:
                    # print '%8d - not valid' % v
                    if options.verbose:
                        print('  [%8d]  ' % v, end=' ')
                    else:
                        print('  ', end=' ')
                    print('0x%08x' % 0)
                    pt.insert(v, -1)
                    done = 1
        print('')

        #
        # now, need to generate virtual address trace
        #

        addrList = []
        if addresses == '-1':
            # need to generate addresses
            for i in range(0, options.num):
                n = int(asize * random.random())
                addrList.append(n)
        else:
            addrList = addresses.split(',')

        print('虚拟地址追踪:')
        for vStr in addrList:
            # vaddr = int(asize * random.random())
            vaddr = int(vStr)
            if not options.solve:
                print('  VA 0x%08x (decimal: %8d) --> PA or invalid address?' % (vaddr, vaddr))
            else:
                paddr = 0
                # split vaddr into VPN | offset
                vpn = (vaddr & vpnmask) >> pagebits
                if pt[vpn] < 0:
                    print('  VA 0x%08x (decimal: %8d) -->  Invalid (VPN %d not valid)' % (vaddr, vaddr, vpn))
                else:
                    pfn = pt[vpn]
                    offset = vaddr & pagemask
                    paddr = (pfn << pagebits) | offset
                    print('  VA 0x%08x (decimal: %8d) --> %08x (decimal %8d) [VPN %d]' % (vaddr, vaddr, paddr, paddr, vpn))
        print('')

        if not options.solve:
             print('对每个虚拟地址, 写下它对应的物理地址, 或者写下它是否越界')


def main():
    core = LinearTranslateCore()


if __name__ == "__main__":
    main()
